<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div onclick="isonclick()">点击</div>
<script>
    //创建虚拟DOM
    //   添加指定属性
    const setAttribute = (node, key, value) => {
        switch (key) {
            case 'style':
                node.style.cssText = value
                break
            case 'value':
                let tagName = node.tagName || ''
                //string.toLowerCase()字符串转换为小写
                tagName = tagName.toLowerCase()
                if (tagName === 'input' || tagName === 'textarea') {
                    node.value = value
                } else {
                    //如果节点不是input 或者textarea,则使用 setAttribute 去设置属性
                    node.setAttribute(key, value)
                }
                break;
            default:
                node.setAttribute(key, value)
                break
        }
    }

    class Element {
        constructor(tagName, attributes = {}, children = []) {
            this.tagName = tagName
            this.attributes = attributes
            this.children = children
        }

        render() {
            let element = document.createElement(this.tagName)
            let attributes = this.attributes
            for (let key in attributes) {
                setAttribute(element, key, attributes[key])
            }
            let children = this.children
            children.forEach(child => {
                let childElement = child instanceof Element
                    ? child.render() //若 child 也就是虚拟节点，递归进行
                    : document.createTextNode(child) // 若是字符串,直接创建文本节点
                element.appendChild(childElement)
            })
            return element
        }
    }

    function element(tagName, attributes, children) {
        return new Element(tagName, attributes, children)
    }

    const renderDom = (element, target) => {
        target.appendChild(element)
    }

    const diff = (oldVirtualDom, newVirtialDom) => {
        let patches = {}
        //递归树 比较后的结果放到patches
        walkToDiff(oldVirtualDom, newVirtialDom)
        return patches

    }
    let initialIndex = 0
    const walkToDiff = (oldVirtualDom, newVirtualDom, index, patches) => {
        console.log(oldVirtualDom, newVirtualDom, index, patches, '对比数据前的操作')
        let diffResult = []
        //如果newVitualDom 不存在,说明该节点被移除，我们将type为remove的对象推进diffResult 变量，并记录index
        if (!newVirtualDom) {
            diffResult.push({
                type: 'REMOVE',
                index
            })
        }
        //如果新旧节点都是文本节点,是字符串
        else if (typeof oldVirtualDom === 'string' && typeof newVirtualDom === 'string') {
            //比较文本是否相同,如果不同则记录新的结果
            if (oldVirtualDom !== newVirtualDom) {
                diffResult.push({
                    type: 'NODIFY_TEXT',
                    data: newVirtualDom,
                    index
                })
            }
            //如果新旧节点类型相同
            else if (oldVirtualDom.tagName === newVirtualDom.tagName) {
                //比较属性是否相同
                let diffAttributeResult = {}
                for (let key in newVirtualDom) {
                    if (oldVirtualDom[key] !== newVirtualDom[key]) {
                        diffAttributeResult[key] = newVirtualDom[key]
                    }
                }
                for (let key in newVirtualDom) {
                    //旧节点不存在的新属性
                    if (!oldVirtualDom.hasOwnProperty(key)) {
                        diffAttributeResult[key] = newVirtualDom[key]
                    }
                }
                if (Object.keys(diffAttributeResult).length > 0) {
                    diffResult.push({
                        type: 'MODIFY_ATTRIBUTES',
                        diffAttributeResult
                    })
                }
                //如果有子节点 ,遍历子节点
                oldVirtualDom.children.forEach((child, index) => {
                    walkToDiff(child, newVirtualDom.children[index], ++initialIndex, patches)
                })
            }
            //else 说明节点类型不同 被直接替换了，我们直接将新的结果，push
            else {
                diffResult.push({
                    type: 'REPLACE',
                    newVirtualDom
                })
            }
            if (!oldVirtualDom) {
                diffResult.push({
                    type: 'REPLACE',
                    newVirtualDom
                })
            }
            if (diffResult.length) {
                patches[index] = diffResult
            }
        }
    }
    const chapterListVirtualDom = element('ul', {id: 'list'}, [
        element('li', {class: 'chapter'}, ['小小小胖1', '小小小胖1-1']),
        element('li', {class: 'chapter'}, ['小小小胖2']),
        element('li', {class: 'chapter'}, ['小小小胖3']),
    ])
    const chapterListVirtualDom1 = element('ul', {id: 'list'}, [
        element('li', {class: 'chapter2'}, ['chapter4']),
        element('li', {class: 'chapter2'}, ['chapter5']),
        element('li', {class: 'chapter2'}, ['chapter5']),
    ])
    const patch = (node, patches) => {
        let walker = {index: 0}
        walk(node, walker, patches)
    }
    const walk = (node, walker, patches) => {
        let currentPatch = patches[walker.index]
        let childNodes = node.childNodes
        childNodes.forEach(child => {
            walker.index++
            walk(child, walker, patches)
        })
        if (currentPatch) {
            doPatch(node, currentPatch)
        }
    }
    const doPatch = (node, patches) => {
        patches.forEach(patch => {
            switch (patch.type) {
                case 'MODIFY_ATTRIBUTES':
                    const attributes = patch.diffAttributeResult.attributes
                    for (let key in attributes) {
                        if (node.nodeType !== 1) return
                        const value = attributes[key]
                        if (value) {
                            setAttribute(node, key, value)
                        } else {
                            node.removeAttribute(key)
                        }
                    }
                    break
                case 'MODIFY_TEXT':
                    node.textContent = patch.data
                    break
                case 'REPLACE':
                    let newNode = (patch.newNode instanceof Element) ? render(patch.newNode) : document.createTextNode(patch.newNode)
                    node.parentNode.removeChild(node)
                    break
                case 'REMOVE':
                    node.parentNode.removeChild(node)
                    break
                default:
                    break
            }
        })
    }


    var element = chapterListVirtualDom.render()
    renderDom(element, document.body)
    // const patches = diff(chapterListVirtualDom, chapterListVirtualDom1)
    // patch(element, patches)
    function isonclick() {
        alert('111')
        const patches = diff(chapterListVirtualDom, chapterListVirtualDom1)
        console.log(patches, '999')
        patch(element, patches)
        // var element = chapterListVirtualDom1.render()
        // renderDom(element, document.body)
    }


</script>
</body>
</html>